﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using WebAnalyser.UI;
using ThreadState = System.Threading.ThreadState;

namespace WebAnalyser.Library
{
    public delegate void ThreadDied(int id);

    public class Web
    {
        private string _result;
        private readonly List<List<string>> _globalUriList = new List<List<string>>();
        private int _total;
        private static double _progression;
        private int _mainLine;
        private int _lol;

        private void UpdateUI(int progression, int step, int total)
        {
            Common.MainWindow.Dispatcher.Invoke(
                DispatcherPriority.Normal,
                new Action(delegate
                    {
                        if (step == 0)
                        {
                            Common.MainWindow.resultProgressBar.IsIndeterminate = false;
                            Common.MainWindow.resultProgressBar.Value = (progression * 100) / total;
                            TaskbarManager.Instance.SetProgressValue((progression * 100) / total, 100);
                            _progression = (progression * 100) / total;
                        }
                        Common.MainWindow.statusLabel.Content = string.Format("Collecting data.. {0} ~{1}%", _total, _progression);
                    }));
        }

        private static void LogResult(string uri, Exception e)
        {
            FileStream test = new FileStream("errors.txt", FileMode.Append);
            StreamWriter test1 = new StreamWriter(test);
            string res = DateTime.Now.Month + "/" + DateTime.Now.Day + "/" + DateTime.Now.Year + " " + DateTime.Now.Hour + ":" + DateTime.Now.Minute + ":" + DateTime.Now.Second + " - " + e.GetType() + ": " + e.Message + "( " + uri + " )" + Environment.NewLine;
            test1.Write(res);
            test1.Close();
            test.Close();
        }

        private static List<string> RemoveDoubleUri(IEnumerable<string> list)
        {
            List<string> newList = new List<string>();

            foreach (string uri in list)
            {
                if (!newList.Contains(uri))
                {
                    newList.Add(uri);
                }
            }
            return newList;
        }

        private List<string> RemoveAlreadyVisitedUri(IEnumerable<string> list)
        {
            List<string> newList = new List<string>();

            foreach (string uri in list)
            {
                bool isPresent = false;
                foreach (List<string> t in _globalUriList)
                {
                    if (t.Contains(uri))
                    {
                        isPresent = true;
                    }
                }
                if (!isPresent)
                {
                    newList.Add(uri);
                }
            }
            return newList;
        }

        private void Request(object data)
        {
            //
            // BEGIN TIMERS
            //
            Stopwatch sw1 = new Stopwatch();
            sw1.Start();
            Stopwatch sw2 = new Stopwatch();
            sw2.Start();
            Stopwatch sw3 = new Stopwatch();
            sw3.Start();
            Stopwatch sw4 = new Stopwatch();
            sw4.Start();
            Stopwatch sw5 = new Stopwatch();
            sw5.Start();
            //
            //
            //
            HttpWebRequest request;
            HttpWebResponse response;
            Stream stream;
            StreamReader streamReader;
            object[] newData = data as object[];

            try
            {
                request = (HttpWebRequest)WebRequest.Create((string)newData[0]);
                request.AllowAutoRedirect = true;
                request.UserAgent = "ZB-Analyser/" + Assembly.GetExecutingAssembly().GetName().Version;
                response = (HttpWebResponse)request.GetResponse();
                stream = response.GetResponseStream();
                streamReader = new StreamReader(stream, Encoding.GetEncoding(850));
                _result = streamReader.ReadToEnd();
                streamReader.Close();
                stream.Close();
                response.Close();
                // ----------
                sw1.Stop();
                // ----------

                Regex urlRegex = new Regex(Common.UrlPattern, RegexOptions.Compiled);
                Match match = urlRegex.Match(response.ResponseUri.AbsoluteUri);
                Common.CurrentUrl = match.Groups;
                // ----------
                sw2.Stop();
                // ----------

                Regex hrefRegex = new Regex(Common.HrefPattern, RegexOptions.Compiled);
                if (response.ContentType.Contains("text/html"))
                {
                    string[] tmp = _result.Split(new[] { "<body" }, StringSplitOptions.RemoveEmptyEntries);
                    MatchCollection testMatch = hrefRegex.Matches(tmp[1]);
                    // ----------
                    sw3.Stop();
                    // ----------

                    List<string> uriList = Common.MatchesToList(testMatch);
                    uriList = Parser.ParseUriList(uriList);
                    uriList = Parser.CleanUriList(uriList);
                    uriList = Parser.GenerateUri(uriList);
                    // ----------
                    sw4.Stop();
                    // ----------
                    uriList = RemoveDoubleUri(uriList);
                    _listMutex.WaitOne();
                    uriList = RemoveAlreadyVisitedUri(uriList);
                    _globalUriList.Add(uriList);
                    _total += uriList.Count;
                    _listMutex.ReleaseMutex();
                    Data.Instance.InsertData((string)newData[0], "");
                    // ----------
                    sw5.Stop();
                    // ----------
                    if ((int)newData[1] < 1)
                    {
                        foreach (string uri in uriList)
                        {
                            if (!Common.CancellationPending)
                            {
                                //UpdateUI(0, 1, _globalUriList[0].Count);
                                Request(new object[] {uri, (int) newData[1] + 1});
                            }
                        }
                    }
                }
                _lol++;
            }
            /*catch (ThreadAbortException abortException)
            {
                Debug.WriteLine("THREAD ID: " + (int) newData[2] + " abortException");
                return;
            }*/
            catch (Exception e)
            {
                _fileMutex.WaitOne();
                LogResult((string) newData[0], e);
                _fileMutex.ReleaseMutex();
                Data.Instance.InsertData((string)newData[0], e.GetType() + ": " + e.Message);
            }
            /*if ((int) newData[2] != -1)
            {
                Debug.WriteLine("SENDING EVENT FROM THREAD: " + newData[2]);
                _eventMutex.WaitOne();
                ThreadDied((int) newData[2]);
                _eventMutex.ReleaseMutex();
                Debug.WriteLine("EVENT SENT BY THREAD:" + newData[2]);
            }*/
        }

        private readonly Semaphore _mainPool = new Semaphore(0, 1);
        private readonly Thread[] _threads = new Thread[100];
        private readonly Mutex _eventMutex = new Mutex();
        private readonly Mutex _listMutex = new Mutex();
        private readonly Mutex _fileMutex = new Mutex();
        private event ThreadDied ThreadDiedEvent;

        private void ThreadDied(int id)
        {
            if (ThreadDiedEvent != null)
            {
                ThreadDiedEvent(id);
            }
        }

        private void StartThreads(int index)
        {
            if (_mainLine == _globalUriList[0].Count || Common.CancellationPending)
            {
                if (!_mainPool.WaitOne(0, true))
                {
                    _mainPool.Release(1);
                }
                return;
            }
            if (_threads[index].ThreadState != ThreadState.Unstarted)
            {
                Debug.WriteLine("WAIT THREAD " + index);
                _threads[index].Join();
                Debug.WriteLine("THREAD KILLED " + _threads[index].ThreadState);
                _threads[index] = new Thread(Request);
                _threads[index].Name = "GRAB THREAD " + index;
            }
            _threads[index].Start(new object[] { _globalUriList[0][_mainLine], 1, index });
            _mainLine++;
        }

        private delegate void Test(int id);

        private int _tmp;

        private void Listenner(int id)
        {
            Debug.WriteLine("RECEIVED ID: " + id);
            new Test(StartThreads).BeginInvoke(id, null, null);
            UpdateUI(_tmp, 0, _globalUriList[0].Count);
            _tmp++;
        }

        public void Grab(object data)
        {
            Request(new[] {data, 4, -1});
            Common.TotalLinks = _total;
            Common.AnalysisStatus = 0;
            /*for (int i = 0; i < _globalUriList[0].Count; i++)
            {
                if (i < 100)
                {
                    _threads[i] = new Thread(Request);
                    _threads[i].Name = "GRAB THREAD " + i;
                }
            }
            for (int j = 0; j < 100; j++)
            {
                StartThreads(j);
            }*/
            /*for (int i = 0; i < _globalUriList[0].Count; i++)
            {
                if (!Common.CancellationPending)
                {
                    Request(new object[] {_globalUriList[0][i], 1, -1});
                    UpdateUI(i, 0, _globalUriList[0].Count);
                }
            }*/
            //UpdateUI(_lol, 0, _globalUriList[0].Count);
            Parallel.For(0, _globalUriList[0].Count, i =>
                                                         {
                                                             if (Common.CancellationPending) return;
                                                             UpdateUI(_lol, 0, _globalUriList[0].Count);
                                                             Request(new object[] { _globalUriList[0][i], 1, -1 });
                                                         });
            //ThreadDiedEvent += new ThreadDied(Listenner);
            //_mainPool.WaitOne();
/*            for (int i = 0; i < _globalUriList[0].Count; i++)
            {
                if (!Common.CancellationPending)
                {
                    //Request(new object[] { _globalUriList[0][i], 1 });
                    //UpdateUI(i, 0, _globalUriList[0].Count);
                    for (int j = 0; j < 100; j++)
                    {
                    }
                }
            }*/
            /*//
            // BEGIN TIMERS
            //
            Stopwatch sw1 = new Stopwatch();
            sw1.Start();
            Stopwatch sw2 = new Stopwatch();
            sw2.Start();
            Stopwatch sw3 = new Stopwatch();
            sw3.Start();
            Stopwatch sw4 = new Stopwatch();
            sw4.Start();
            Stopwatch sw5 = new Stopwatch();
            sw5.Start();
            //
            //
            //
            HttpWebRequest request;
            HttpWebResponse response;
            Stream stream;
            StreamReader streamReader;
            object[] newData = data as object[];

            try
            {
                request = (HttpWebRequest) WebRequest.Create((string) newData[0]);
                request.AllowAutoRedirect = true;
                request.UserAgent = "ZB-Analyser/" + Assembly.GetExecutingAssembly().GetName().Version;
                response = (HttpWebResponse) request.GetResponse();
                stream = response.GetResponseStream();
                streamReader = new StreamReader(stream, Encoding.GetEncoding(850));
                _result = streamReader.ReadToEnd();
                streamReader.Close();
                stream.Close();
                response.Close();
                // ----------
                sw1.Stop();
                // ----------

                Regex urlRegex = new Regex(Common.UrlPattern, RegexOptions.Compiled);
                Match match = urlRegex.Match(response.ResponseUri.AbsoluteUri);
                Common.CurrentUrl = match.Groups;
                // ----------
                sw2.Stop();
                // ----------

                Regex hrefRegex = new Regex(Common.HrefPattern, RegexOptions.Compiled);
                if (response.ContentType.Contains("text/html"))
                {
                    string[] tmp = _result.Split(new[] {"<body"}, StringSplitOptions.RemoveEmptyEntries);
                    MatchCollection testMatch = hrefRegex.Matches(tmp[1]);
                    // ----------
                    sw3.Stop();
                    // ----------

                    List<string> uriList = Common.MatchesToList(testMatch);
                    uriList = Parser.ParseUriList(uriList);
                    uriList = Parser.CleanUriList(uriList);
                    uriList = Parser.GenerateUri(uriList);
                    // ----------
                    sw4.Stop();
                    // ----------
                    uriList = RemoveDoubleUri(uriList);
                    uriList = RemoveAlreadyVisitedUri(uriList);
                    _globalUriList.Add(uriList);
                    _total += uriList.Count;
                    Data.Instance.InsertData((string) newData[0], "");
                    // ----------
                    sw5.Stop();
                    // ----------
                    if ((int)newData[1] < 1)
                    {
                        for (int i = 0; i < uriList.Count; i++)
                        {
                            if (!Common.CancellationPending)
                            {
                                UpdateUI(i, (int) newData[1], uriList.Count);
                                Grab(new object[] {uriList[i], (int) newData[1] + 1});
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                LogResult((string) newData[0], e);
                Data.Instance.InsertData((string) newData[0], e.GetType() + ": " + e.Message);
            }
            Common.TotalLinks = _total;
            Common.AnalysisStatus = 0;
            if (Common.CancellationPending)
            {
                Common.AnalysisStatus = 1;
            }*/
        }
    }
}